package Encryption;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.xml.bind.DatatypeConverter;

public class Encryptor {

	public Encryptor() throws NoSuchAlgorithmException,
			InvalidKeySpecException, IOException {
		KeyPair kp = newKeyPair(3072); // 2048 bit RSA; might take a second to
										// generate keys
		PublicKey pubKey = kp.getPublic();
		PrivateKey privKey = kp.getPrivate();
		KeyFactory fact = KeyFactory.getInstance("RSA");
		RSAPublicKeySpec pub = fact.getKeySpec(pubKey, RSAPublicKeySpec.class);
		RSAPrivateKeySpec priv = fact.getKeySpec(privKey,
				RSAPrivateKeySpec.class);
		saveToFile("public1.key", pub.getModulus(), pub.getPublicExponent());
		saveToFile("private1.key", priv.getModulus(), priv.getPrivateExponent());
		pubKey = readKeyFromFile("public1.key");
		privKey = readPrivateKeyFromFile("private1.key");
	}
	
	public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException {
		//Encryptor enc = new Encryptor();
		PrivateKey privKey  = readPrivateKeyFromFile("private1.key");
		String x= "wassim";
		byte[] s = x.getBytes();
		byte[] s2 = encryptWithPubKey(s);
		//System.out.println(new String(decryptWithPrivKey(s2)));
		System.out.println(privKey.toString());
	}

	public static byte[] h2b(String hex) {
		return DatatypeConverter.parseHexBinary(hex);
	}

	public static String b2h(byte[] bytes) {
		return DatatypeConverter.printHexBinary(bytes);
	}

	private static SecureRandom sr = new SecureRandom();

	public static KeyPair newKeyPair(int rsabits)
			throws NoSuchAlgorithmException {
		KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
		generator.initialize(rsabits, sr);
		return generator.generateKeyPair();
	}

	public static byte[] encryptWithPubKey(byte[] input)
			throws IllegalBlockSizeException, BadPaddingException,
			InvalidKeyException, NoSuchAlgorithmException,
			NoSuchPaddingException, IOException {
		PublicKey key = readKeyFromFile("/Users/wassim/Documents/workspace/Security/public1.key");
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, key);
		return cipher.doFinal(input);
	}

	public static byte[] decryptWithPrivKey(byte[] input,BigInteger exp)
			throws IllegalBlockSizeException, BadPaddingException,
			InvalidKeyException, NoSuchAlgorithmException,
			NoSuchPaddingException, IOException, InvalidKeySpecException {
		PrivateKey key = createPrivateKey(exp);
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, key);
		return cipher.doFinal(input);
	}

	public static void saveToFile(String fileName, BigInteger mod,
			BigInteger exp) throws IOException {
		ObjectOutputStream oout = new ObjectOutputStream(
				new BufferedOutputStream(new FileOutputStream(fileName)));
		try {
			oout.writeObject(mod);
			oout.writeObject(exp);
		} catch (Exception e) {
			throw new IOException("Unexpected error", e);
		} finally {
			oout.close();
		}
	}

	static PublicKey readKeyFromFile(String keyFileName) throws IOException {
		InputStream in = new FileInputStream(new File(keyFileName));
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(
				in));
		try {
			BigInteger m = (BigInteger) oin.readObject();
			BigInteger e = (BigInteger) oin.readObject();
			RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
			KeyFactory fact = KeyFactory.getInstance("RSA");
			PublicKey pubKey = fact.generatePublic(keySpec);
			return pubKey;
		} catch (Exception e) {
			throw new RuntimeException("Spurious serialisation error", e);
		} finally {
			oin.close();
		}
	}

	public static PrivateKey readPrivateKeyFromFile(String keyFileName)
			throws IOException {
		InputStream in = new FileInputStream(new File(keyFileName));
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(
				in));
		try {
			BigInteger m = (BigInteger) oin.readObject();
			BigInteger e = (BigInteger) oin.readObject();
			RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
			KeyFactory fact = KeyFactory.getInstance("RSA");
			PrivateKey privKey = fact.generatePrivate(keySpec);
			return privKey;
		} catch (Exception e) {
			throw new RuntimeException("Spurious serialisation error", e);
		} finally {
			oin.close();
		}
	}
	
	public static BigInteger readPrivateKeyFromFile()
			throws IOException {
		InputStream in = new FileInputStream(new File("/Users/wassim/Documents/workspace/Security/private1.key"));
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(
				in));
		try {
			BigInteger m = (BigInteger) oin.readObject();
			BigInteger e = (BigInteger) oin.readObject();
			RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
			KeyFactory fact = KeyFactory.getInstance("RSA");
			PrivateKey privKey = fact.generatePrivate(keySpec);
			return e;
		} catch (Exception e1) {
			throw new RuntimeException("Spurious serialisation error", e1);
		} finally {
			oin.close();
		}
	}
	
	public static BigInteger readKeyFromFile()
			throws IOException {
		InputStream in = new FileInputStream(new File("/Users/wassim/Documents/workspace/Security/public1.key"));
		ObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(
				in));
		try {
			BigInteger m = (BigInteger) oin.readObject();
			BigInteger e = (BigInteger) oin.readObject();
			RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, e);
			KeyFactory fact = KeyFactory.getInstance("RSA");
			PrivateKey privKey = fact.generatePrivate(keySpec);
			return m;
		} catch (Exception e1) {
			throw new RuntimeException("Spurious serialisation error", e1);
		} finally {
			oin.close();
		}
	}
		
	public static PrivateKey createPrivateKey(BigInteger exponent) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException{
		BigInteger m = readKeyFromFile();
		RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(m, exponent);
		KeyFactory fact = KeyFactory.getInstance("RSA");
		PrivateKey privKey = fact.generatePrivate(keySpec);
		return privKey;
	}
	

}
